// Test to verify delete endpoint returns correct response format
// This test ensures the 204 No Content response is handled properly by frontend

use serde_json::Value;
use std::sync::Arc;
use std::time::Duration;
use tempfile::TempDir;
use tokio::time::timeout;

use axum::{
    extract::DefaultBodyLimit,
    http::Method,
    routing::{delete, get, post, put},
    Router,
};
use rdm_rust::config::AppConfig;
use rdm_rust::database::connection::establish_connection_pool;
use rdm_rust::handlers::{auth, data};
use rdm_rust::services::{AuthService, DataService, DvcService, StorageService};
use rdm_rust::AppState;
use reqwest::multipart;
use tower::ServiceBuilder;
use tower_http::{
    cors::{Any, CorsLayer},
    trace::TraceLayer,
};

// Test configuration
const TEST_SERVER_PORT: u16 = 0; // Use random available port
const TEST_TIMEOUT: Duration = Duration::from_secs(30);

struct TestServer {
    base_url: String,
    client: reqwest::Client,
}

impl TestServer {
    async fn new() -> Self {
        // Create temporary directory for test storage
        let temp_dir = TempDir::new().expect("Failed to create temp dir");

        // Load test configuration
        let mut config = AppConfig::from_env().expect("Failed to load config");
        config.server.port = TEST_SERVER_PORT;
        config.dvc.storage_path = temp_dir.path().to_string_lossy().to_string();

        // Initialize database
        let db_pool = establish_connection_pool(&config.database.url)
            .await
            .expect("Failed to establish database connection");

        // Initialize services
        let auth_service = Arc::new(AuthService::new(db_pool.clone(), config.clone()));
        let data_service = Arc::new(DataService::new(db_pool.clone()));
        let storage_service = Arc::new(StorageService::new(config.dvc.storage_path.clone()));
        let dvc_service = Arc::new(DvcService::new(db_pool.clone()));

        let app_state = AppState {
            auth_service,
            data_service,
            storage_service,
            dvc_service,
        };

        // Build application router (same as main.rs)
        let public_routes = Router::new()
            .route("/api/register", post(auth::register))
            .route("/api/register-admin", post(auth::register_admin))
            .route("/api/login", post(auth::login))
            .route("/api/data/public", get(data::list_public_data_items));

        let protected_routes = Router::new()
            .route("/api/user-info", get(auth::user_info))
            .route("/api/data", get(data::list_data_items))
            .route("/api/data/upload", post(data::upload_data))
            .route("/api/data/:id", get(data::get_data_item))
            .route("/api/data/:id", delete(data::delete_data_item))
            .route("/api/data/:id/download", get(data::download_data_file))
            .route("/api/data/:id/dvc", get(data::download_dvc_file))
            .route("/api/data/:id/dvc/content", get(data::get_dvc_content))
            .route("/api/data/:id/metadata", put(data::upload_metadata))
            .route_layer(axum::middleware::from_fn_with_state(
                app_state.clone(),
                rdm_rust::middleware::auth::auth_middleware,
            ));

        let app = Router::new()
            .merge(public_routes)
            .merge(protected_routes)
            .layer(
                ServiceBuilder::new()
                    .layer(TraceLayer::new_for_http())
                    .layer(
                        CorsLayer::new()
                            .allow_origin(Any)
                            .allow_methods([
                                Method::GET,
                                Method::POST,
                                Method::PUT,
                                Method::DELETE,
                                Method::OPTIONS,
                            ])
                            .allow_headers(Any),
                    )
                    .layer(DefaultBodyLimit::max(100 * 1024 * 1024)), // 100MB max file size
            )
            .with_state(app_state);

        // Start server on random port
        let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
            .await
            .expect("Failed to bind to address");
        let port = listener
            .local_addr()
            .expect("Failed to get local address")
            .port();

        // Spawn server task
        tokio::spawn(async move {
            axum::serve(listener, app)
                .await
                .expect("Failed to serve server");
        });

        // Give server time to start
        tokio::time::sleep(Duration::from_millis(100)).await;

        let base_url = format!("http://127.0.0.1:{}", port);
        let client = reqwest::Client::builder()
            .timeout(TEST_TIMEOUT)
            .build()
            .expect("Failed to create HTTP client");

        Self { base_url, client }
    }

    async fn wait_for_server(&self) -> Result<(), Box<dyn std::error::Error>> {
        let health_url = format!("{}/api/data/public", self.base_url);

        for _ in 0..10 {
            match timeout(Duration::from_secs(1), self.client.get(&health_url).send()).await {
                Ok(Ok(response)) if response.status().is_success() => return Ok(()),
                Ok(_) => (),
                Err(_) => (),
            }
            tokio::time::sleep(Duration::from_millis(100)).await;
        }

        Err("Server failed to start".into())
    }

    async fn register_and_login(&self, email: &str, password: &str) -> String {
        // Register user
        let register_response = self
            .client
            .post(format!("{}/api/register", self.base_url))
            .json(&serde_json::json!({
                "email": email,
                "password": password
            }))
            .send()
            .await
            .expect("Failed to send register request");

        assert!(
            register_response.status().is_success(),
            "User registration should succeed"
        );

        // Login to get auth token
        let login_response = self
            .client
            .post(format!("{}/api/login", self.base_url))
            .json(&serde_json::json!({
                "email": email,
                "password": password
            }))
            .send()
            .await
            .expect("Failed to send login request");

        assert_eq!(login_response.status(), 200, "Login should succeed");

        let login_body: Value = login_response
            .json()
            .await
            .expect("Failed to parse login response");
        let auth_token = login_body
            .get("access_token")
            .or_else(|| login_body.get("token"))
            .and_then(|t| t.as_str())
            .expect("Login response should contain token");

        auth_token.to_string()
    }
}

#[tokio::test]
async fn test_delete_response_format() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Setup: Register and login user
    let test_email = format!(
        "responseformat{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Upload a test file with the problematic filename
    let test_content = "Attack tool detection application report 0516.pptx content".as_bytes();
    let form = multipart::Form::new()
        .part(
            "files",
            multipart::Part::bytes(test_content.to_vec())
                .file_name("攻击工具检测申请立项汇报0516.pptx")
                .mime_str(
                    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
                )
                .expect("Invalid mime type"),
        )
        .text("project", "攻击工具检测项目")
        .text("source", "https://example.com/attack-tool-detection")
        .text("description", "攻击工具检测申请立项汇报");

    let upload_response = server
        .client
        .post(format!("{}/api/data/upload", server.base_url))
        .header("Authorization", format!("Bearer {}", auth_token))
        .multipart(form)
        .send()
        .await
        .expect("Failed to upload test file");

    assert_eq!(upload_response.status(), 200, "File upload should succeed");

    let upload_body: Value = upload_response
        .json()
        .await
        .expect("Failed to parse upload response");
    let uploaded_items = upload_body.get("items").unwrap().as_array().unwrap();
    let uploaded_item = &uploaded_items[0];
    let item_id = uploaded_item.get("id").unwrap().as_i64().unwrap();

    println!(
        "Uploaded file: 攻击工具检测申请立项汇报0516.pptx (ID: {})",
        item_id
    );

    // Test delete response format
    let delete_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send delete request");

    // Verify response status is 204 No Content
    assert_eq!(
        delete_response.status(),
        204,
        "Delete should return 204 No Content"
    );

    // Verify response has no content (this is what causes the frontend JSON parsing error)
    let response_text = delete_response
        .text()
        .await
        .expect("Failed to read response text");
    assert!(
        response_text.is_empty(),
        "204 response should have empty body"
    );

    println!("✓ Delete response correctly returns 204 No Content with empty body");

    // Verify that trying to parse as JSON would fail (simulating frontend behavior)
    let delete_response_again = server
        .client
        .delete(format!("{}/api/data/999", server.base_url)) // Non-existent ID
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send delete request for non-existent file");

    assert_eq!(
        delete_response_again.status(),
        404,
        "Delete of non-existent file should return 404"
    );

    // 404 response should have JSON body
    let error_body: Value = delete_response_again
        .json()
        .await
        .expect("Failed to parse error response");
    assert!(
        error_body.get("detail").is_some(),
        "404 response should have error detail"
    );

    println!("✓ 404 response correctly returns JSON error body");
}

#[tokio::test]
async fn test_frontend_simulation() {
    let server = TestServer::new().await;
    server
        .wait_for_server()
        .await
        .expect("Test server failed to start");

    // Setup: Register and login user
    let test_email = format!(
        "frontendtest{}@hillstonenet.com",
        chrono::Utc::now().timestamp_nanos_opt().unwrap_or(0)
    );
    let test_password = "testpassword123";
    let auth_token = server.register_and_login(&test_email, test_password).await;

    // Upload a test file
    let test_content = b"Frontend simulation test content";
    let form = multipart::Form::new()
        .part(
            "files",
            multipart::Part::bytes(test_content.to_vec())
                .file_name("frontend_test.txt")
                .mime_str("text/plain")
                .expect("Invalid mime type"),
        )
        .text("project", "Frontend Test")
        .text("source", "test://frontend")
        .text("description", "Test for frontend simulation");

    let upload_response = server
        .client
        .post(format!("{}/api/data/upload", server.base_url))
        .header("Authorization", format!("Bearer {}", auth_token))
        .multipart(form)
        .send()
        .await
        .expect("Failed to upload test file");

    assert_eq!(upload_response.status(), 200, "File upload should succeed");

    let upload_body: Value = upload_response
        .json()
        .await
        .expect("Failed to parse upload response");
    let uploaded_items = upload_body.get("items").unwrap().as_array().unwrap();
    let uploaded_item = &uploaded_items[0];
    let item_id = uploaded_item.get("id").unwrap().as_i64().unwrap();

    // Simulate frontend behavior
    let delete_response = server
        .client
        .delete(format!("{}/api/data/{}", server.base_url, item_id))
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send delete request");

    // Frontend check: response should be successful
    assert!(
        delete_response.status().is_success(),
        "Response should be successful"
    );

    // Frontend check: response.status should be 204
    assert_eq!(
        delete_response.status(),
        204,
        "Response status should be 204"
    );

    // Simulate the problematic frontend code that tries to parse JSON
    // This should fail for 204 responses
    let json_result: Result<Value, reqwest::Error> = delete_response.json().await;

    match json_result {
        Ok(_) => panic!("Expected JSON parsing to fail for 204 response"),
        Err(e) => {
            println!("✓ JSON parsing correctly failed for 204 response: {}", e);
            // This is expected behavior - the frontend fix handles this case
        }
    }

    // Simulate the fixed frontend code
    let delete_response_fixed = server
        .client
        .delete(format!("{}/api/data/999", server.base_url)) // Non-existent
        .header("Authorization", format!("Bearer {}", auth_token))
        .send()
        .await
        .expect("Failed to send delete request");

    if delete_response_fixed.status().is_success() {
        let result = if delete_response_fixed.status() != 204 {
            Some(
                delete_response_fixed
                    .json::<Value>()
                    .await
                    .expect("Failed to parse JSON"),
            )
        } else {
            None
        };

        if result.is_none() {
            println!("✓ Fixed frontend code correctly handles 204 response");
        }
    } else {
        let error: Value = delete_response_fixed
            .json()
            .await
            .expect("Failed to parse error JSON");
        println!("✓ Error response correctly parsed: {}", error);
    }
}
